Parameter Passing and Stack Frames
http://asm.tsx.org In this article, I hope to show how parameters are passed to function in assembly language, as well as how they are addressed from inside the function. I will also describe stack frames, local stack variables, and how to use them. Finally, I will show how all this info is useless, as your assembler can do it for you all automatically :) Parameter Passing Conventions A parameter passing convention defines how arguments are passed to a function. In the Win32 API, two different conventions are used: stdcall and C. Stdcall is used for every API function, except for wsprintf, which uses the C calling convention.
Sdtcall calling convention
; Example of calling a stdcall function: ; MessageBeep prototype: ; int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); push MB_OK ; uType push offset szCaption ; lpCaption push offset szText ; lpText push [hWnd] ; hWnd call MessageBoxAIt can be seen that the parameters are passed in reverse, and that there is no need adjust the stack manually. Note that all Win32 API functions (except wsprintf) use this convention.
C calling convention
; Example of calling a C function: ; wsprintf prototype: int cdecl wsprintf(LPTSTR lpOut, LPCTSTR lpFmt, ...); push 345h ; vararg 2 push 1 ; vararg 1 push offset szFormat ; lpFmt push offset szOutput ; lpOut call wsprintf add esp, 16 ; clean stackHere we see that we can pass as many arguments as we like. However we must also clean the stack ourselves. In this example, 4 DWORD arguments were passed, so we have to add 4 * sizeof(DWORD), which equalss 16, to the stack pointer ESP. Stack Frames Stack Frames are used by high-level languages to handle parameter passing and local (stack) variables. It provides a simple method for addressing the parameters and variables. The following diagram shows a memory map for a typical function. In this diagram, EBP points to the address 00640010. You can see that local variables can be accessed as negative offsets from this address. Likewise, passed function parameters can be accessed as positive offsets from it.
To get the values of the passed parameters or of the local variables, all that
needs to be done is the following:
Using a stack frame allows parameters and local variables to be easily accessed. It also means that you can continue to push and then pop values on and off of the stack for whatever reason, without destroying the contents of your saved variables. Note that it is unwise to change the value of EBP, unless there is a valid reason for doing so. Changing its value without restoring it, will most likely result in a corruption of the stack, and the program will crash.
Stack Frames are used extensively in high level languages. In assembly language
however, they are not used as frequently. In assembly language you can pass
parameters through the registers. Since it is not always mecessary to pass
parmeters by using the stack, there is not always a need for a stack frame.
However, if your function requires local variables, a stack frame will be necessary,
regardless of the method used for passing parameters. You should remember, though,
since we are using assembly language, we want to utilize the registers as much as
possible, so the use of stack variables should be minimized when possible.
; MyProc: Takes 2 DWORD paramaters, passed on the stack ; Uses 2 DWORD local variables MyProc proc push ebp ; preserve EBP mov ebp, esp ; now set EBP to the current stack pointer sub esp, 8 ; adjust esp to point beyond 2 local variables mov eax,dword ptr [ebp-08] ; copy local variable 2 into eax mov ebx,dword ptr [ebp+08] ; copy parameter 1 into ebx add esp,8 ; adjust esp to boint before the two local ; variables (point it to saved ebp) pop ebp ; restore ebp ret 8 ; return from function call and ; adjust stack past the 2 parameters MyProc endpThis function manually sets up the stack frame, by pushing EBP and then adjusting the stack pointer ESP. It also manually cleans up the stack frame, by restoring EBP. This method, however can be greatly simplified by the use of 2 different instructions, ENTER and LEAVE. These instructions were designed to do exactly what we have just done manually. The ENTER and companion LEAVE instructions are provided to support block structured languages. The ENTER instruction (when used) is typically the first instruction in a procedure and is used to set up a new stack frame for a procedure. The LEAVE instruction is then used at the end of the procedure, right before the RET instruction, to release the stack frame.
ENTER <size>, <nesting level>
LEAVE
This example shows how to use ENTER and LEAVE to create a stack frame. This code functions identical to the previous example (MyProc), but is simpler, and runs faster. You can see that it greatly simplifies the code. ; MyProc2: Takes 2 DWORD paramaters, passed on the stack ; Uses 2 DWORD local variables MyProc2 proc enter 8, 0 ; set up stack frame ; set aside sapce for 2 local DWORDS mov eax,dword ptr [ebp-08] ; copy local variable 2 into eax mov ebx,dword ptr [ebp+08] ; copy parameter 1 into ebx leave ; clean up stack frame ret 8 ; return from function call and ; adjust stack past the 2 parameters MyProc2 endp Assembler Features Assemblers such as MASM and TASM can furthur simplify parameter passing and local variable usage. By specify the calling convention in the .model directive, the assembler will take care of all of the details of creating the stack frame. This is accomplished by the including the following .model directive:
.model flat, stdcall This tells the assembler that every function uses the stdcall convention unless otherwise specified. The assembler will then insert the ENTER and LEAVE instructions to create the stack frames where appropriate. You can also specify the calling convention on a function by function basis, as seen by the following code:
MyFunction proc stdcall param1:DWORD, param2:DWORD ... MyFunction endp Assemblers also allow for a much cleaner method of accessing local variables. Instead of referring to them as offsets of EBP, the assembler allows you to access them by name. The assembler will then translate this higher level syntax to the same low level instructions as we have seen, An example of how MASM or TASM simplifies locals follows:
MyFunction proc stdcall param1:DWORD, param2:DWORD LOCAL var1:DWORD ; alias to [ebp - 4] LOCAL var2:DWORD ; alias to [ebp - 8] mov eax, [var1] ; assembles to: mov eax, byte ptr [ebp - 8] ret MyFunction endp Conclusion
We have seen how to pass paramaters, access them, and use local stack variables.
We have also learned how we can use the features of our assembler to make these
concepts far easier to use.
|